package re

import "utf8"
import "fmt"
import "bytes"

type chars struct {rune int; rest string}

func StartStream( source string ) *chars {
  result := &chars{0, source}
  result.advance()
  return result
}

const MARK = 0x40000000

const (
  RPAR = MARK | ')'
  HYPHEN = MARK | '-'
  HAT = MARK | '^'
  ALT = MARK | '|'
  LPAR = MARK | '('
  LBOX = MARK | '['
  RBOX = MARK | ']'
  STAR = MARK | '*'
  PLUS = MARK | '+'
  QUEST = MARK | '?'
)

/*
  advance x to the next character. if that is a backslash,
  advance x to the next character and decode it if alphabetic.
  plain characters are marked with 0x10000.
*/
func (x *chars) advance() {
  if len(x.rest) == 0 { 
    x.rune = 0
  } else { 
    rune, size := utf8.DecodeRuneInString( x.rest )
    x.rest = x.rest[size:]
    if rune == '\\' {
      rune, size = utf8.DecodeRuneInString( x.rest )
      x.rest = x.rest[size:]
      switch rune {
        case 't': rune = '\t'
        case 'n': rune = '\n'
        case 's': rune = ' '
        default:
      }
    } else {
      rune |= MARK
    }
    x.rune = rune
  }
}

func Parse( x string ) RE {
  return parse( StartStream( x ) )
}

func parse( x *chars ) RE {
  i, v := 0, make(map[int]RE)
  for x.rune != RPAR && x.rune != 0 { 
    v[i] = parseSeq( x )
    i += 1
    if x.rune == ALT { x.advance() }
  }
  return toSlice(v, func (s []RE) RE { return Alt(s) } )
}

func parseTerm( x *chars ) RE {
  switch x.rune {
    case LPAR:
        x.advance()
        e := parse( x )
        if x.rune == RPAR { x.advance() } else { fmt.Printf( "OOPS" ) }
        return e
        
    case LBOX:
        var chars bytes.Buffer
        var prev int = 0
        inverted := false
        x.advance()
        if x.rune == HAT { inverted = true; x.advance() }
        for (x.rune != RBOX && x.rune != 0) {
          if x.rune == HYPHEN {
            x.advance()
            // fmt.Printf( ">> range from %c to %c\n", prev, x.rune &^ MARK )
            for b := prev; b <= x.rune &^ MARK; b += 1 { chars.WriteRune( b ) }
          } else {
            prev = x.rune &^ MARK
            chars.WriteRune( prev )
          }
          x.advance()
        }
        if x.rune == RBOX { x.advance() }
        return AnyOf{chars.String(), inverted}
  }
  defer x.advance()
  return AChar{x.rune &^ MARK}
}

func parseSeq( x *chars ) RE {
  i, v := 0, make(map[int]RE)
  for x.rune != ALT && x.rune != RPAR && x.rune != 0 {
    v[i] = parseTerm( x )
    i += 1
    if x.rune == STAR || x.rune == PLUS || x.rune == QUEST {
      ch := x.rune &^ MARK
      x.advance()
      v[i-1] = REP{ch, v[i-1]}
    }
  }
  return toSlice(v, func(s []RE) RE {return Seq(s)} )
}

func toSlice( v map[int]RE, f func([]RE)RE ) RE {
  if len(v) == 1 {
    return v[0]
  } else {
    z := make([]RE, len(v))
    for i, re := range v {z[i] = re}
    return f(z)
  }
  return AChar{0}
}
